﻿using System;
using System.Globalization;
using System.Windows.Input;

using Microsoft.VisualStudio.NetworkEmulation.Properties;
using Microsoft.VisualStudio.QualityTools.NetworkEmulation;

namespace Microsoft.VisualStudio.NetworkEmulation
{
    internal abstract class NetworkEmulationCommand : ICommand
    {
        public NetworkEmulationCommand()
        {
            NetworkEmulationState.GetInstance().DriverStateChanged += 
                new EventHandler(NetworkEmulationCommand_DriverStateChanged);
            
            // Signal the command to initialize the state.
            NetworkEmulationCommand_DriverStateChanged(this, new EventArgs());
        }

        ~NetworkEmulationCommand()
        {
            if (null != NetworkEmulationState.PeekInstance())
            {
                NetworkEmulationState.GetInstance().DriverStateChanged -=
                    new EventHandler(NetworkEmulationCommand_DriverStateChanged);
            }
        }

        #region ICommand

        public abstract bool CanExecute(object parameter);
        public abstract void Execute(object parameter);
        public event EventHandler CanExecuteChanged;

        #endregion

        protected void NetworkEmulationCommand_DriverStateChanged(object sender, EventArgs e)
        {
            if (null != CanExecuteChanged)
            {
                CanExecuteChanged(sender, e);
            }
        }

        #region Properties

        // Convience Property
        protected DriverState DriverState
        {
            get { return NetworkEmulationState.GetInstance().DriverState; }
            set
            {
                NetworkEmulationState.GetInstance().DriverState = value;
            }
        }

        // Convience Property
        protected NetworkEmulationDriver Driver
        {
            get { return NetworkEmulationState.GetInstance().Driver; }
        }

        #endregion
    }

    internal class NetworkEmulationStartCommand : NetworkEmulationCommand
    {
        public override bool CanExecute(object parameter)
        {
            return DriverState == DriverState.Initialized; 
        }

        public override void Execute(object parameter)
        {
            try
            {
                string profile = (string)parameter;
                try
                {
                    if (Driver.LoadProfile(profile))
                    {
                        if (Driver.StartEmulation())
                        {
                            DriverState = DriverState.Emulating;
                        }
                        else
                        {
                            // Most errors in starting/stopping the driver throw an exception.  
                            // However, this case would signal a general failure to stop emulation. 
                            throw new NetworkEmulationException();
                        }
                    }
                    else
                    {
                        NetworkEmulationState.GetInstance().SignalErrorState(Resources.ErrorLoadingProfile);
                    }
                }
                catch (NetworkEmulationDriverNotInstalledException)
                {
                    NetworkEmulationState.GetInstance().SignalErrorState(Resources.ErrorDriverNotInstalled);
                }
                catch (NetworkEmulationErrorProcessingProfileException)
                {
                    NetworkEmulationState.GetInstance().SignalErrorState(Resources.ErrorProcessingProfile);
                }
                catch (System.IO.FileNotFoundException)
                {
                    string message = string.Format(CultureInfo.CurrentCulture, Resources.ErrorEmulationNotStarted);
                    NetworkEmulationState.GetInstance().SignalErrorState(message);
                }
            }
            catch (Exception)
            {
                NetworkEmulationState.GetInstance().SignalErrorState(Resources.ErrorStartEmulation);
            }
        }
    }

    internal class NetworkEmulationStopCommand : NetworkEmulationCommand
    {
        public override bool CanExecute(object parameter)
        {
            return DriverState.Equals(DriverState.Emulating);
        }

        public override void Execute(object parameter)
        {
            try
            {
                // Force network emulation to stop if it doesn't return after 1 second.
                // TODO: Should probably move this to a configuration setting.
                if (Driver.StopEmulation(1000, true))
                {
                    DriverState = DriverState.Initialized;
                }
                else
                {
                    // Most errors in starting/stopping the driver throw an exception.  
                    // However, this case would signal a general failure to stop emulation. 
                    throw new NetworkEmulationException();
                }
            }
            catch (NetworkEmulationException)
            {
                NetworkEmulationState.GetInstance().SignalErrorState(Resources.ErrorStartEmulation);
            }
        }
    }
}
